type Direction = "up" | "down" | "left" | "right";

// const matrix = [
//   [0, 2, 2, 0],
//   [0, 0, 0, 0],
//   [2, 4, 4, 2],
//   [2, 4, 4, 4],
// ];

export function moveBy(matrix: number[][], direction: Direction) {
  const row = matrix.length,
    col = matrix[0].length;

  // 判断坐标(i, j)是否在矩阵范围内
  function _inRange(i: number, j: number) {
    // 如果i大于等于0且小于行数，且j大于等于0且小于列数，则返回true，否则返回false
    return i >= 0 && i < row && j >= 0 && j < col;
  }

  // 定义一个对象，包含四个方法，分别表示向上、向下、向左、向右移动
  const nexts = {
    // 向上移动，返回新的坐标
    up: (i: number, j: number) => [i + 1, j],
    // 向下移动，返回新的坐标
    down: (i: number, j: number) => [i - 1, j],
    // 向左移动，返回新的坐标
    left: (i: number, j: number) => [i, j + 1],
    // 向右移动，返回新的坐标
    right: (i: number, j: number) => [i, j - 1],
  };

  // 定义一个函数，用于获取下一个非零元素的位置和值
  function _nextNonZero(i: number, j: number) {
    // 获取下一个元素的位置
    let [nextI, nextJ] = nexts[direction](i, j);
    // 如果下一个元素的位置不在范围内，则返回null
    if (!_inRange(nextI, nextJ)) return null;
    // 循环遍历下一个元素的位置
    while (_inRange(nextI, nextJ)) {
      // 获取下一个元素的值
      const val = matrix[nextI][nextJ];
      // 如果下一个元素的值不为0，则返回该元素的值和位置
      if (val !== 0) {
        return [nextI, nextJ, val];
      }
      // 获取下一个元素的位置
      [nextI, nextJ] = nexts[direction](nextI, nextJ);
    }
    // 如果遍历完所有元素，都没有找到非零元素，则返回null
    return null;
  }

  function _move(i: number, j: number) {
    if (!_inRange(i, j)) return;
    const next = _nextNonZero(i, j);
    if (!next) {
      return;
    }
    const [nextI, nextJ, val] = next;
    if (matrix[i][j] === 0) {
      matrix[i][j] = val;
      matrix[nextI][nextJ] = 0;
      _move(i, j);
    } else if (matrix[i][j] === val) {
      matrix[i][j] *= 2;
      matrix[nextI][nextJ] = 0;
    }
    const [ni, nj] = nexts[direction](i, j);
    _move(ni, nj);
  }

  if (direction === "up") {
    for (let j = 0; j < col; j++) {
      _move(0, j);
    }
  } else if (direction === "down") {
    for (let j = 0; j < col; j++) {
      _move(row - 1, j);
    }
  } else if (direction === "left") {
    for (let i = 0; i < row; i++) {
      _move(i, 0);
    }
  } else if (direction === "right") {
    for (let i = 0; i < row; i++) {
      _move(i, col - 1);
    }
  }
}

// moveBy(matrix, "up");
// print(matrix);
